package com.ss.zsensm;

import java.util.ArrayList;
import java.util.List;

import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.Bundle;
import android.app.Activity;
import android.util.Log;
import android.view.Menu;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.util.DisplayMetrics;

public class MainActivity extends Activity implements SensorEventListener {
	
	final private int MAXNUMBUTTON = 10;
	final private int SENSTIVITY = 20;
	
	private DisplayMetrics dm;
	private int widthPixels;
	private int heightPixels;
	private int gamePadLayoutWidthPixels;
	private int gamePadLayoutHeightPixels;
	
	private byte[] data;
	private int dataOffset;
	
	private TextView btnInitialize;

	private DataTransmission dt;
	private Thread dtThread;
	private DataCollector dc;
	
	
	RelativeLayout rlayout;
	TextView tvLayoutInfo;
	
	private int[] touch;
	
	private int globalId = 0;
	
	private int nWidget;
	List<ZSWidget> widgetList;
	ZSOrientationXWidget zsowx;
	ZSOrientationYWidget zsowy;
	
	List<ZSButtonWidget> buttonWidgetList;
	
	boolean orientationListener;
	
	String layoutName = "";
    @Override
	protected void onCreate(Bundle savedInstanceState) {
    	
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		// disable sleep
		getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
		
		btnInitialize = (TextView)findViewById(R.id.btnInitialize);
	
		touch = new int[MAXNUMBUTTON];
		widgetList = new ArrayList<ZSWidget>();
		buttonWidgetList = new ArrayList<ZSButtonWidget>();
		
		rlayout = (RelativeLayout)findViewById(R.id.layoutGamepad);
		tvLayoutInfo = (TextView) findViewById(R.id.tvInfo);
		
		dm = new DisplayMetrics();
		
		getWindowManager().getDefaultDisplay().getMetrics(dm);
		widthPixels = getApplicationContext().getResources().getDisplayMetrics().widthPixels;
		heightPixels = getApplicationContext().getResources().getDisplayMetrics().heightPixels;
		gamePadLayoutWidthPixels = 0;
		gamePadLayoutHeightPixels = 0;
		
		initialize();
	
		dc = new DataCollector(this, SENSTIVITY);
		dt = new DataTransmission(this, dc);
		dtThread = new Thread(dt);
		dtThread.start();
	}
    
    public void OnConnectClicked(View view) {
    	this.reInitialize();
    	tvLayoutInfo.setText("No Layout");
    }
    @Override
    public void onWindowFocusChanged(boolean hasFocus) {
    	if (gamePadLayoutWidthPixels == 0) {
    		RelativeLayout gamepadLayout = (RelativeLayout)findViewById(R.id.layoutGamepad);
    		gamePadLayoutWidthPixels = gamepadLayout.getMeasuredWidth();
    		gamePadLayoutHeightPixels = gamepadLayout.getMeasuredHeight();
    	}	
        super.onWindowFocusChanged(hasFocus);
    }
    
    public void release() {
    	dt.interruptSocket();
    	dtThread.interrupt();
    	
    	try {
			dtThread.join();
		} catch (InterruptedException e) {
			Log.i("zsensm", "ma.release.ie: " + e.getMessage());
		}
    	
    	for (int i = 0; i < widgetList.size(); i++) {
    		widgetList.get(i).uninstall();
    	}
    }
    
    public int getScreenWidth() {
    	return widthPixels;
    }
    
    public int getScreenHeight() {
    	return heightPixels;
    }
    
    public int getLayoutWidth() {
    	return gamePadLayoutWidthPixels;
    }
    
    public int getLayoutHeight() {
    	return gamePadLayoutHeightPixels;
    }
    
    public void initialize() {
    	nWidget = 0;
		dataOffset = 0;
		orientationListener = false;
		
		widgetList.clear();
		buttonWidgetList.clear();
		
		zsowx = null;
		zsowy = null;
		
		globalId = 0;
		
		for (int i = 0; i < MAXNUMBUTTON; i++) {
			touch[i] = -1;
		}
    }
    
    @Override
    public void onStop() {
    	super.onStop();
    	release();
    	finish();
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }
    
    public RelativeLayout getLayout(){
    	return rlayout;
    }
    
    public byte[] getCollectedData(){
    	return data;
    }
    
    public void reInitialize() {
    	release();
    	initialize();
    	
    	// data transmission thread restart
    	dtThread = new Thread(dt);
    	dtThread.start();
    }

	@Override
	public void onAccuracyChanged(Sensor arg0, int arg1) {
	}

	@Override
	public void onSensorChanged(SensorEvent event) {
		if (event.sensor.getType() == Sensor.TYPE_ORIENTATION) {
			if (zsowx != null) {
				zsowx.OnChange(event.values[1]);
			}
			if (zsowy != null) {
				zsowy.OnChange(event.values[2]);
			}
		}
	}

	@Override
	public boolean onTouchEvent(MotionEvent event) {
		int action = event.getAction();
		int actionMasked = event.getActionMasked();
		if (actionMasked == MotionEvent.ACTION_MOVE) {
			return super.onTouchEvent(event);
		}

		int pointerIndex = action >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
		int pointerId = event.getPointerId(pointerIndex);

		int x = (int) event.getX(pointerIndex), 
				y = (int) event.getY(pointerIndex);
		switch (actionMasked) {
		case (MotionEvent.ACTION_UP):
		case (MotionEvent.ACTION_POINTER_UP):
			for (int i = 0; i < buttonWidgetList.size(); i++) {
				if (buttonWidgetList.get(i).onPointerUp(touch[pointerId]))
						break;
			}
			break;
		case (MotionEvent.ACTION_DOWN):
			for (int i = 0; i < MAXNUMBUTTON; i++)
				touch[i] = -1;
		//deliberately ignore break
		case (MotionEvent.ACTION_POINTER_DOWN):
			for (int i = 0; i < buttonWidgetList.size(); i++) {
				int btnId;
				if ((btnId = buttonWidgetList.get(i).onPointerDown(x, y)) >= 0) {
					touch[pointerId] = btnId;
					break;
				}
			}
			break;
		case (MotionEvent.ACTION_CANCEL):
			break;
		}

		return super.onTouchEvent(event);
	}
	
	public void setLayoutName(String _layoutName) {
		layoutName = _layoutName;
	}

	public boolean addWidget(int widgetType, ZSWidget widget) {
		++nWidget;
		
		widgetList.add(widget);
		dataOffset = widget.register(dataOffset);
		switch (widgetType){
		case PacketInfo.ZS_WIDGET_BUTTON:
			buttonWidgetList.add((ZSButtonWidget)widget);
			break;
		case PacketInfo.ZS_WIDGET_ORIENTATION_X:
			zsowx = (ZSOrientationXWidget) widget;
			break;
		case PacketInfo.ZS_WIDGET_ORIENTATION_Y:
			zsowy = (ZSOrientationYWidget) widget;
			break;
		}
		return true;
	}
	
	public void unRegisterOrientationListener() {
		if (orientationListener == true) {
			SensorManager sm = (SensorManager) getSystemService(SENSOR_SERVICE);
			sm.unregisterListener(this);
			orientationListener = false;
		}
	}
	
	public void registerOrientationListener() {
		if (orientationListener == false) {
			SensorManager sm = (SensorManager) getSystemService(SENSOR_SERVICE);
			sm.registerListener(this, sm.getDefaultSensor(Sensor.TYPE_ACCELEROMETER), SensorManager.SENSOR_DELAY_GAME);
			sm.registerListener(this, sm.getDefaultSensor(Sensor.TYPE_ORIENTATION), SensorManager.SENSOR_DELAY_GAME);
			orientationListener = true;
		}
	}
	
	public void initializeLayout() {
		runOnUiThread(new Runnable() {
			public void run() {
				for (int i = 0; i < nWidget; i++) {
					widgetList.get(i).uninstall();
				}
				initialize();
				synchronized(dt) {
					dt.notify();
				}
			}
		});
	}
	
	public void commitLayout(byte uniqueId){	
		data = new byte[dataOffset + 6];
		data[0] = PacketInfo.ZS_DATA;
		//data[0..4] is reserved for hash
		data[5] = (byte) uniqueId;
		for (int i = 0; i < dataOffset; i++)
			data[i + 6] = (byte) 0;
		
		runOnUiThread(new Runnable() { 
			public void run() {
				tvLayoutInfo.setText(layoutName);
				for (int i = 0; i < nWidget; i++) {
					widgetList.get(i).install();
				}
			}
		});	
	}
	
	public void OnDataChange(int idx, byte x) {
		data[idx + 6] = x;
	}
	
	public int getGlobalId() {
		return globalId++;
	}
}
